﻿using Microsoft.Win32;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Text;
using System.IO;
using System.Net;
using System.Text;
using bigormScreensaver;

namespace bigormscreensaver
{
    public class Settings
    {
        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            try
            {
                string[] regVals = reg_Key.GetValueNames();
                Array.Sort(regVals);
                string sLastVal = "";
                foreach (string regVal in regVals)
                {
                    int iCommonChars = 0;
                    int iAfterLastCommonSpace = 0;
                    while (iCommonChars < regVal.Length && iCommonChars < sLastVal.Length && regVal[iCommonChars] == sLastVal[iCommonChars])
                        if (sLastVal[iCommonChars++] == ' ' || sLastVal.Length == iCommonChars)
                            iAfterLastCommonSpace = iCommonChars;
                    if (sb.Length > 0)
                        sb.Append(" | ");
                    sb.Append(regVal.Substring(iAfterLastCommonSpace).Trim());
                    sb.Append("=");
                    object v = reg_Key.GetValue(regVal);
                    string s = v.GetType().ToString();
                    if (v.GetType().ToString() == "System.String[]")
                        sb.Append(String.Join(" ", (string[])v));
                    else
                        sb.Append(v.ToString());
                    sLastVal = regVal;
                }
            }
            catch (Exception) { }
            return sb.ToString();
        }
        #region Registry
        private RegistryKey _reg_Key;
        public RegistryKey reg_Key
        {
            get
            {
                if (_reg_Key == null)
                    _reg_Key = Registry.CurrentUser.OpenSubKey("SOFTWARE\\bigorm", true);
                return _reg_Key;
            }
        }
        private object Reg_GetValue(string valueName, object defaultValue)
        {
            try
            {
                object o = reg_Key.GetValue(valueName);
                if (o != null)
                    return reg_Key.GetValue(valueName);
            }
            catch (Exception)
            {
            }
            return defaultValue;
        }
        private bool Reg_SetValue(string valueName, object value, RegistryValueKind kind)
        {
            bool bRes = true;
            try
            {
                if (reg_Key == null)
                    _reg_Key = Registry.CurrentUser.CreateSubKey("SOFTWARE\\bigorm");
                reg_Key.SetValue(valueName, value, kind);
            }
            catch (Exception)
            {
                bRes = false;
            }
            return bRes;
        }
        private string Reg_GetString(string valueName, string defaultValue)
        {
            return (string)Reg_GetValue(valueName, defaultValue);
        }
        private bool Reg_SetString(string valueName, string value)
        {
            return Reg_SetValue(valueName, value, RegistryValueKind.String);
        }
        private string[] Reg_GetMultiString(string valueName, string[] slDefaultValues)
        {
            return (string[])Reg_GetValue(valueName, slDefaultValues);
        }
        private bool Reg_SetMultiString(string valueName, string[] values)
        {
            return Reg_SetValue(valueName, values, RegistryValueKind.MultiString);
        }
        private string[][] Reg_GetMultiMultiString(string valueName, string[][] sllDefaultValues)
        {
            List<string[]> sllRes = new List<string[]>();
            try
	        {
                foreach (string sValue in reg_Key.GetSubKeyNames())
                    if (sValue.Length >= valueName.Length && sValue.Substring(0, valueName.Length) == valueName.Substring(0, valueName.Length))
                        sllRes.Add((string[])Reg_GetValue(valueName, new string[]{}));
	        }
	        catch (Exception)
	        {
                return sllDefaultValues;
            }
            if (sllRes.Count == 0)
                return sllDefaultValues;
            else
                return sllRes.ToArray();
        }
        private bool Reg_SetMultiMultiString(string valueName, string[][] values)
        {
            return Reg_SetValue(valueName, values, RegistryValueKind.MultiString);
        }
        private Int64 Reg_GetQWord(string valueName, Int64 defaultValue)
        {
            return (Int64)Reg_GetValue(valueName, defaultValue);
        }
        private bool Reg_SetQWord(string valueName, Int64 value)
        {
            return Reg_SetValue(valueName, value, RegistryValueKind.QWord);
        }
        #endregion Registry
        #region Mode
        public string Mode_Selected
        {
            get { return Reg_GetString("screensaver", "Text"); }
            set { Reg_SetString("screensaver", value); }
        }
        public int Mode_SelectedIndex {
            get {
                string sMode = Mode_Selected.ToUpper();
                if (sMode == "Image".ToUpper()) return 0;
                else if (sMode == "LinkedIn".ToUpper()) return 1;
                else if (sMode == "Network".ToUpper()) return 2;
                else if (sMode == "Feed".ToUpper()) return 3;
                else if (sMode == "Text".ToUpper()) return 4;
                else return 4; 
            }
            set { 
                string sMode = Mode_Selected.ToUpper();
                if (value == 0) Mode_Selected = "Image";
                else if (value == 1) Mode_Selected = "LinkedIn";
                else if (value == 2) Mode_Selected = "Network";
                else if (value == 3) Mode_Selected = "Feed";
                else if (value == 4) Mode_Selected = "Text";
                else
                    Mode_Selected = "Text";
            }
        }
        #endregion Mode
        #region Image
        public class CImages: IEnumerable<string>
        {
            public class ImageLocator : BackgroundWorker, IDisposable
            { // For a path, look through in the background...
                private ImageLocator()
                {
                }
                public string [][]llFindStrings;
                public ImageLocator(string findSpec, string[][] llFindStrings)
                {
                    this.FindSpec = findSpec;
                    this.llFindStrings = llFindStrings;
                    DoWork +=Image_Locator_DoWork;
                    WorkerReportsProgress = true;
                    WorkerSupportsCancellation = true;
                    ProgressChanged += Image_Locator_ProgressChanged;
                    RunWorkerAsync();
                }
                void Image_Locator_ProgressChanged(object sender, ProgressChangedEventArgs e)
                {
                    Items.Add((string)e.UserState);
                }
                void Image_Locator_DoWork(object sender, DoWorkEventArgs e)
                { // Folder: recurse and find all files | uri find all images
                    FindItems(FindSpec);
                }
                private bool FindItems(string sFind)
                {
                    if (CancellationPending)
                        return false;
                    bool bRes = false;
                    if (sFind.Length > 2)
                        if (sFind.IndexOf("://") > -1)
                            bRes = FindItems_Web(sFind);
                        else if (sFind[sFind.Length - 1] == '\\' || Directory.Exists(sFind))
                            bRes = FindItems_Folder(sFind);
                        else if (sFind.IndexOfAny("?*".ToCharArray()) >= 0)
                            bRes = FindItems_Files(sFind);
                        else 
                            bRes = FindItems_File(sFind, false);
                    return bRes;
                }
                private bool FindItems_File(string sFile, bool bFound = true)
                {
                    if (CancellationPending)
                        return false;
                    if (!bFound)
                        bFound = File.Exists(sFile);
                    if (bFound)
                        Items.Add(sFile);
                    return bFound;
                }
                private bool FindItems_Files(string sFind)
                { // Create folder and filter from sFind
                    if (CancellationPending)
                        return false;
                    int iLastBSlash = sFind.LastIndexOf('\\');
                    if (iLastBSlash == -1)
                        return false;
                    else if (iLastBSlash == sFind.Length - 1)
                        return FindItems_Folder(sFind.Substring(0, iLastBSlash));
                    else
                        return FindItems_Folder(sFind.Substring(0, iLastBSlash), sFind.Substring(iLastBSlash + 1));
                }
                private bool FindItems_Folder(string sFolder, string[] saFilter)
                {
                    if (CancellationPending)
                        return false;
                    if (saFilter.Length == 0)
                        return false;
                    bool bRes = true;
                    try
                    {
                        DirectoryInfo di = new DirectoryInfo(sFolder);
                        // Get files for every filter
                        foreach (string sFilter in saFilter)
                            if (CancellationPending)
                                break;
                            else
                                foreach (FileInfo fi in di.GetFiles(sFilter))
                                    if (CancellationPending)
                                        break;
                                    else if (!FindItems_File(fi.FullName))
                                        bRes = false;
                        // Recurse directories
                        foreach (var diSub in di.GetDirectories())
                            if (CancellationPending)
                                break;
                            else
                                if (!FindItems_Folder(diSub.FullName, saFilter))
                                    bRes = false;
                    }
                    catch (Exception)
                    {
                        bRes = false;
                    }
                    return bRes;
                }
                private bool FindItems_Folder(string sFolder, string sFilter = "")
                {
                    if (CancellationPending)
                        return false;
                    if (sFilter == "")
                        sFilter = "*.png|*.jpg|*.jpeg|*.gif|*.bmp|*.wmf";
                    string [] slFilter = sFilter.Split("|".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                    return FindItems_Folder(sFolder, slFilter);
                }
                private bool FindItems_Web(string sFind)
                {
                    if (CancellationPending)
                        return false;
                    bool bRes = true;
                    try
                    {
                        WebRequest request = WebRequest.Create(sFind);
                        WebResponse response = request.GetResponse();
                        if (CancellationPending)
                            return false;
                        else if (response.ContentType.Substring(0, 5) == "text/")
                        { // We have text: parse html and return all the images
                            Stream data = response.GetResponseStream();
                            if (CancellationPending)
                                return false;
                            using (StreamReader sr = new StreamReader(data))
                            {
                                Util.HtmlImageUriExtractor uriExtractor = new Util.HtmlImageUriExtractor(sr, 102400, llFindStrings);
                                string uri;
                                bool bFound;
                                while (!CancellationPending && uriExtractor.LookForNextUri(out uri, out bFound))
                                    if (bFound)
                                        if (!FindItems_Link(sFind, uri))
                                            break;
                            }
                            data.Close();
                            response.Close();
                            request.Abort();
                        }
                        else if (response.ContentType.Substring(0, 6) == "image/")
                        { // We have media
                            if (CancellationPending)
                                return false;
                            request.Abort();
                            if (CancellationPending)
                                return false;
                            FindItems_File(sFind);
                        }
                    }
                    catch (Exception)
                    {
                        bRes = false;
                    }
                    return bRes & CancellationPending;
                }
                private bool FindItems_Link(string sFindUri, string sImageUri)
                {
                    if (CancellationPending)
                        return false;
                    int iImageUri_c_s_s = sImageUri.IndexOf("://");
                    if (sImageUri.Length > 0 && iImageUri_c_s_s == -1)
                    { // image empty or not x://y
                        if (sImageUri[0] == '/')
                        { // image /x
                            int iFindUri_c_s_s = sFindUri.IndexOf("://");
                            if (iFindUri_c_s_s == -1)
                                return true; // sFind has no ://
                            int iFindUri_c_s_s_s = sFindUri.IndexOf('/', iFindUri_c_s_s + 3);
                            if (iFindUri_c_s_s_s == -1) // sFind is ::/x, return ::x/y
                                Items.Add(sFindUri + sImageUri);
                            else
                                Items.Add(sFindUri.Substring(0, iFindUri_c_s_s_s) + sImageUri);
                            return true;
                        }
                        else if (sImageUri.IndexOf('/') == -1)
                        { // just image name
                            int iFindUri_ls = sFindUri.LastIndexOf('/');
                            if (iFindUri_ls == -1)
                                return true;
                            Items.Add(sFindUri.Substring(0, iFindUri_ls + 1) + sImageUri);
                            return true;
                        }
                        else
                            return true; // Does not seem valid
                    }
                    Items.Add(sImageUri);
                    return true;
                }
                public List<string> Items = new List<string>();
                private string _FindSpec;
                public string FindSpec
                {
                    get { return _FindSpec; }
                    set { _FindSpec = Environment.ExpandEnvironmentVariables(value); }
                }
                protected override void Dispose(bool disposing)
                {
                    base.CancelAsync();
                    base.Dispose(disposing);
                }
            }
            public List<ImageLocator> Locators = new List<ImageLocator>();
            private CImages() { }
            public CImages(string Default, List<string>ImageSearchPaths, string[][] llImageLocatorFindStrings)
            {
                this.llImageLocatorFindStrings = llImageLocatorFindStrings;
                this.Default = Default;
                this.ImageSearchPaths = ImageSearchPaths;
            }
            private string _Default;
            public string Default
            {
                set { _Default = Environment.ExpandEnvironmentVariables(value); }
                get {
                    if (_Default == "")
                    {
                        foreach (ImageLocator il in Locators)
                            while (_Default == "")
                                if (il.Items.Count > 0)
                                    _Default = il.Items[0];
                    }
                    return _Default; 
                }
            }
            public List<string> ImageSearchPaths
            {
                get 
                { // return the "path" for each search
                    List<string> slRes = new List<string>();
                    foreach (ImageLocator il in Locators)
                        slRes.Add(il.FindSpec);
                    return slRes;
                }
                set
                {   // Remove any old we have not represented in new searches
                    List<ImageLocator> iRemove = new List<ImageLocator>();
                    foreach (ImageLocator il in Locators)
                        if (value.IndexOf(il.FindSpec) == -1)
                        {
                            iRemove.Add(il);
                            il.Dispose();
                        }
                    foreach (ImageLocator il in iRemove)
                        Locators.Remove(il);
                    // Add any new we do not have represented in old searches
                    foreach (string sFindSpecCandidate in value)
                    {
                        bool bExistsAlready = false;
                        foreach (ImageLocator il in Locators)
                            if (il.FindSpec == sFindSpecCandidate)
                            {
                                bExistsAlready = true;
                                break;
                            }
                        if (!bExistsAlready)
                            Locators.Add(new ImageLocator(sFindSpecCandidate, llImageLocatorFindStrings));
                    }
                }
            }
            private string[][] _llImageLocatorFindStrings;
            public string[][] llImageLocatorFindStrings
            {
                get { return _llImageLocatorFindStrings; }
                set
                {
                    _llImageLocatorFindStrings = value;
                    foreach (ImageLocator il in Locators)
                        il.llFindStrings = _llImageLocatorFindStrings;
                }
            }
            public void Dispose()
            {
                foreach (ImageLocator il in Locators)
                    il.Dispose();
            }
            IEnumerator<string> IEnumerable<string>.GetEnumerator()
            {
                return new CImagesEnumerator(this);
            }
            public IEnumerator GetEnumerator()
            {
                return new CImagesEnumerator(this);
            }

        }
        public class CImagesEnumerator : IEnumerator<string>
        {
            private int CurrentLocatorIndex = -1;
            private int CurrentIndexInLocator = -1;
            public CImages Images;
            private CImagesEnumerator(){}
            public CImagesEnumerator(CImages Images)
            {
                this.Images = Images; 
            }
            string IEnumerator<string>.Current
            {
                get { return Images.Locators[CurrentLocatorIndex].Items[CurrentIndexInLocator]; }
            }
            public object Current
            {
                get { return Images.Locators[CurrentLocatorIndex].Items[CurrentIndexInLocator]; }
            }
            public CImages.ImageLocator CurrentLocator
            {
                get 
                {
                    if (CurrentLocatorIndex < 0 || CurrentLocatorIndex >= Images.Locators.Count)
                        return null;
                    return Images.Locators[CurrentLocatorIndex]; 
                }
            }
            public bool MoveNext()
            {
                if (Images.Locators.Count == 0)
                    return false;
                if (CurrentLocatorIndex > -1) // Not first
                    if (++CurrentIndexInLocator < Images.Locators[CurrentLocatorIndex].Items.Count) // Not yet at end
                        return true;
                CurrentLocatorIndex++; // Next or first collection...
                CurrentIndexInLocator = 0;
                while (CurrentLocatorIndex < Images.Locators.Count)
                { // Find next collection that has at least one member
                    if (Images.Locators[CurrentLocatorIndex].Items.Count > 0)
                        return true;
                    CurrentLocatorIndex++;
                }
                CurrentLocatorIndex = -1; // reset to before first locator (and show first image)
                return false;
            }
            public bool MovePrevious()
            {
                if (Images.Locators.Count == 0)
                    return false;
                if (CurrentLocatorIndex > -1) // Not first
                    if (--CurrentIndexInLocator >= 0)
                        return true;
                if (--CurrentLocatorIndex < 0) // Previous collection
                {
                    CurrentLocatorIndex = Images.Locators.Count - 1;
                    if (CurrentLocatorIndex == -1)
                        return false; // No collections (at all)
                }
                CurrentIndexInLocator = Images.Locators[CurrentLocatorIndex].Items.Count - 1; // Last item on this (previously previous) collection
                return true;
            }
            public void Reset()
            {
                CurrentIndexInLocator = CurrentLocatorIndex = -1;
            }

            public void Dispose()
            {
            }
        }
        private CImages _Images;
        public CImages Images
        {
            get
            {
                if (_Images == null)
                {
                    _Images = new CImages(Image_PathFirst, new List<string>(Image_Paths), Image_LocatorFindStrings) ;
                }
                return _Images;
            }
        }
        public string[] Image_Paths 
        {
            get { return Reg_GetMultiString("screensaver image paths", new string[] { "%homedrive%%homepath%\\Pictures", "http://www.bing.com/images/search?q=nature&qs=n&form=QBLH" }); }
            set 
            {
                Images.ImageSearchPaths = new List<string>(value);
                Reg_SetMultiString("screensaver image paths", value); 
            }
        }
        public string Image_PathFirst
        {
            get 
            { 
                return Reg_GetString("screensaver image path first", "%homedrive%%homepath%\\Pictures\\"); 
            }
            set 
            {
                Images.Default = value;
                Reg_SetString("screensaver image path first", value); 
            }
        }
        public string[][] Image_LocatorFindStrings
        {
            get {
                return Reg_GetMultiMultiString("screensaver image find strings", new string[][] 
                { 
                    new string[]{"imgurl:&quot;", "", "&quot;" }
                }); 
            }
            set
            {
                Images.llImageLocatorFindStrings = value;
                Reg_SetMultiMultiString("screensaver image find strings", value);
            }
        }
        public string Image_DisplayStyle
        {
            get { return Reg_GetString("screensaver image display", "zoom"); }
            set { Reg_SetString("screensaver image display", value); }
        }
        public Color Image_Backcolor
        {
            get
            {
                return ColorTranslator.FromHtml(Image_BackcolorString);
            }
            set
            {
                Image_BackcolorString = ColorTranslator.ToHtml(value);
            }
        }
        private string Image_BackcolorString
        {
            get { return Reg_GetString("screensaver image background color", "#008040"); }
            set { Reg_SetString("screensaver image background color", value); }
        }
        public int Image_IntervalForTimer
        {
            get
            {
                double dRet = Util.UTime.FromString(Image_IntervalString).TotalMilliseconds;
                if (dRet >= int.MaxValue)
                    return int.MaxValue;
                else if (dRet < 1)
                    return 1;
                return (int)dRet;
            }
        }
        public string Image_IntervalString
        {
            get { return Reg_GetString("screensaver image update interval", "1 min"); }
            set { Reg_SetString("screensaver image update interval", value); }
        }
        #endregion Image
        #region Text
        public string Text_Caption {
            get { return Reg_GetString("screensaver text caption", "Right-click for configuration settings\nThe red fox jumps over the lazy dog"); }
            set { Reg_SetString("screensaver text caption", value); }
        }
        public Font Text_FontForPreview
        {
            get
            {
                return new Font(Text_FontFace, Text_FontSize / 5
                , Text_FontFace.IndexOf(" Italic") > -1 ? FontStyle.Italic
                    : Text_FontFace.IndexOf(" Bold") > -1 ? FontStyle.Bold : FontStyle.Regular);
            } 
        }
        public Font Text_Font 
        {
            get
            {
                return new Font(Text_FontFace, Text_FontSize
                    , Text_FontFace.IndexOf(" Italic") > -1 ? FontStyle.Italic
                        : Text_FontFace.IndexOf(" Bold") > -1 ? FontStyle.Bold : FontStyle.Regular);
            } 
        }
        public string Text_FontFace {
            get { return Reg_GetString("screensaver text face", "Vivaldi"); }
            set { Reg_SetString("screensaver text face", value); }
        }
        public Int64 Text_FontSize {
            get { return Reg_GetQWord("screensaver text size", 48); }
            set { Reg_SetQWord("screensaver text size", value); }
        }
        public Color Text_Color
        {
            get 
            {
                return ColorTranslator.FromHtml(Text_ColorString);
            }
            set
            {
                Text_ColorString = ColorTranslator.ToHtml(value);
            }
        }
        private string Text_ColorString
        {
            get { return Reg_GetString("screensaver text color", "#008040"); }
            set { Reg_SetString("screensaver text color", value); }
        }
        public Color Text_Backcolor
        {
            get 
            {
                return ColorTranslator.FromHtml(Text_BackcolorString);
            }
            set
            {
                Text_BackcolorString = ColorTranslator.ToHtml(value);
            }
        }
        private string Text_BackcolorString
        {
            get { return Reg_GetString("screensaver text backcolor", "Black"); }
            set { Reg_SetString("screensaver text backcolor", value); }
        }
        public Random Text_rand = new Random();
        public int Text_IntervalForTimer 
        { 
            get 
            {
                double dRet = Util.UTime.FromString(Text_IntervalString).TotalMilliseconds;
                if (dRet >= int.MaxValue)
                    return int.MaxValue;
                else if (dRet < 1)
                    return 1;
                return (int)dRet;
            }
        }
        public string Text_IntervalString
        {
            get { return Reg_GetString("screensaver text update interval", "1 min"); }
            set { Reg_SetString("screensaver text update interval", value); }
        }
        #endregion Text
        #region Font
        private List<string> _font_availableFaces = null;
        public List<string> font_availableFaces 
        { 
            get 
            {
                if (_font_availableFaces == null)
                {
                    _font_availableFaces = new List<string>();
                    foreach (var ff in new InstalledFontCollection().Families)
                    {
                        if (ff.IsStyleAvailable(FontStyle.Regular))
                            _font_availableFaces.Add(ff.Name);
                        if (ff.IsStyleAvailable(FontStyle.Bold))
                            _font_availableFaces.Add(ff.Name + " Bold");
                        if (ff.IsStyleAvailable(FontStyle.Italic))
                            _font_availableFaces.Add(ff.Name + " Italic");
                    }
                    font_availableFaces.Sort();
                }
                return _font_availableFaces;
            }
        }
        #endregion Font
    }
}